﻿using MonoMod.RuntimeDetour;
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml.Linq;
using UnityEngine;
using static UnityEngine.GraphicsBuffer;

namespace Erika;

public static class CAM
{

    public static Dictionary<Chara, CharaAddon> CharaAddons = new Dictionary<Chara, CharaAddon>();

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static CharaAddon GetAddon(in Chara chara)
    {
        if (CharaAddons.TryGetValue(chara, out CharaAddon value))
            return value;
        else
        {
            var addon = new CharaAddon(chara);
            CharaAddons.Add(chara, addon);
            return addon;
        }
    }
}

public class CharaAddon
{
    public readonly Chara self;
    public CharaAddon(Chara self)
    {
        this.self = self;
    }

    public bool WillConsumeTurn = false;
    public bool LockStamina = false;
    float staminaTimer = 0;
    int lastWorldTime;
    int worldTime;
    int deltaTime;
    public void Tick()
    {
        worldTime = EClass.world.date.GetRaw();
        deltaTime = worldTime - lastWorldTime;

        TryRecover();

        foreach (var c in foodsChara)
        {
            c.Value.MoveImmediate(new Point(0, 0));
            CAM.GetAddon(c.Value).WillConsumeTurn = true;
        }
        //if (EClass.pc == self)
        //    Debug.Log(foodsCard.Count + " " + foodsChara.Count);
        lastWorldTime = worldTime;
    }

    public void TryRecover()
    {
        staminaTimer += deltaTime;// self.END;
        if (self.IsDeadOrSleeping)
        {
            if (self.isDead)
                staminaTimer = 0;

            return;
        }

        if ((staminaTimer >= (100f / self.END)))
        {
            if (self.hunger.GetPhase() < 3)
            {
                var recoverSpCount = Mathf.Floor(staminaTimer / (100f / self.END));
                staminaTimer -= recoverSpCount * (100f / self.END);

                if (self.hunger.GetPhase() <= 1 && self.stamina.max * 2 / 3 + 1 > self.stamina.value)
                {
                    if (self.stamina.max * 2 / 3 + 1 > self.stamina.value)
                        self.stamina.Mod((int)recoverSpCount);

                    if (self.stamina.max * 2 / 3 + 1 < self.stamina.value)
                        self.stamina.value = self.stamina.max * 2 / 3 + 1;
                }
                else if (self.stamina.max / 2 + 1 > self.stamina.value)
                {
                    if (self.stamina.max / 2 + 1 > self.stamina.value)
                        self.stamina.Mod((int)recoverSpCount);

                    if (self.stamina.max / 2 + 1 < self.stamina.value)
                        self.stamina.value = self.stamina.max / 2 + 1;
                }
            }
            else
                staminaTimer = 0;
        }
    }

    public void ResetStaminaTick()
    {
        staminaTimer = 0;
    }



    public enum ActType
    {
        MeleeAtk,
        RangedAtk,
        RangedAtkFailed,
        Throw,
        Reload,
    }

    int staminaBefore;
    public void BeforeAct(ActType actType)
    {
        //记录当前的耐力
        staminaBefore = self.stamina.value;
        if (actType == ActType.MeleeAtk || actType == ActType.RangedAtk)
        {
            LockStamina = true;
        }

        //Plugin.LogInfo("Before time: " + worldTime + " sp: " + staminaBefore);

    }

    public void AfterAct(ActType actType, Chara cc, Card tc)
    {
        LockStamina = false;
        //Plugin.LogInfo("After type " + actType.ToString() + "sp: " + self.stamina.value + " min: " + self.stamina.min + "max: " + self.stamina.max);

        if (staminaBefore > self.stamina.value)
            self.stamina.value = staminaBefore;

        float spCost = 0;


        if (actType == ActType.MeleeAtk)
        {
            //if (Act.CC == EClass.pc)
            //{
            //    //Plugin.LogInfo("tc: " + tc + " is food: " + tc.trait.CanEat(self) + " tarparent: " + tc.parent + " tcGetRootCard: " + tc.GetRootCard() + " tcIsChara " + tc.isChara);
            //    Eat(Act.TC);
            //}

            bool hasweapon = false;
            int numWeapons = 0;
            float dualwield = (float)(cc.elements.GetOrCreateElement(131)).Value;
            foreach (BodySlot slot in cc.body.slots)
            {
                if (!tc.IsAliveInCurrentZone)
                {
                    return;
                }

                if (slot.thing != null && slot.elementId == 35 && slot.thing.source.offense.Length >= 2)
                {
                    Thing weapon = slot.thing;
                    hasweapon = true;
                    var weaponSkill = cc.elements.GetOrCreateElement(weapon.category.skill);

                    spCost += (weapon.SelfWeight + 500f) / ((cc.STR * 5f + weaponSkill.Value * 2f) * 100f) * (numWeapons >= 1 ? (1f / (1f + dualwield / 10f)) : 1f);
                    //Plugin.LogInfo("w: " + weapon.SelfWeight + " str: " + cc.STR + " skill: " + weaponSkill.Value + " num:" + numWeapons + "dualwield " + dualwield + " final: " + (weapon.SelfWeight + 500f) / ((cc.STR * 5f + weaponSkill.Value * 2f) * 100f));
                    numWeapons++;
                }
            }

            if (!hasweapon)
            {
                var skill = cc.elements.GetOrCreateElement(100); // 格斗
                spCost += (float)10f / (cc.END * 6 + skill.Value * 3);
            }
        }
        if (actType == ActType.RangedAtk)
        {
            if (cc.ranged.IsWeapon)
            {
                var ammo = cc.ranged.ammoData;
                int ammoWeight = 0;
                if (ammo == null)
                    ammoWeight = 1;

                var recoil = (float)Math.Max(cc.ranged.source.offense[0], 1) * cc.ranged.c_diceDim;
                var skillGun = cc.elements.GetOrCreateElement(105); // gun

                //Plugin.LogInfo(cc.ranged.NameSimple + " damage " + recoil);
                if (cc.ranged != null)
                {
                    switch (cc.ranged.id)
                    {
                        case "gun":
                            spCost += (float)recoil * 0.08f / (cc.STR * 6 + cc.END * 3 + skillGun.Value * 3);
                            break;
                        case "gun_assault":
                            spCost += (float)recoil * 0.25f / (cc.STR * 7 + cc.END * 4 + skillGun.Value * 4);
                            break;
                        case "gun_rail":
                            spCost += (float)(recoil + ammoWeight * 10f) * 0.15f / (cc.STR * 6 + cc.END * 2 + skillGun.Value * 2);
                            break;
                        default:
                            var weaponSkill = cc.elements.GetOrCreateElement(cc.ranged.category.skill);
                            spCost += (float)(recoil + ammoWeight * 10f) * 0.5f / (cc.STR * 8 + cc.END * 3 + weaponSkill.Value * 3);
                            break;

                    }
                }


            }
        }

        if (spCost == 0)
            return;

        //Plugin.LogInfo("Final: " + spCost);

        var costint = (int)Math.Floor(spCost);
        cc.stamina.Mod(-costint);
        spCost -= costint;
        var rnd = EClass.rndf(1);
        //Plugin.LogInfo("Final: " + spCost + "rnd: " + rnd);

        if (rnd < spCost)
        {
            //Plugin.LogInfo("Cost");
            cc.stamina.Mod(-1);
        }

    }

    HashSet<Card> foodsCard = new HashSet<Card>();

    Dictionary<Card, Chara> foodsChara = new Dictionary<Card, Chara>();




    public void OnConsumedFood(Card food)
    {
        Debug.LogWarning("OnConsumedFood");

        if (foodsCard.Remove(food))
        {
            if (foodsChara.TryGetValue(food, out Chara chara))
            {
                chara.MoveZone(self.currentZone);

                chara.SetHidden(false);
                CAM.GetAddon(chara).WillConsumeTurn = false;
                chara.MoveImmediate(self.pos);
                chara.DamageHP(99999, AttackSource.Trap, self);
                foodsChara.Remove(food);
            }
        }
    }

    public void OnLostFood(Card food)
    {
        Debug.LogWarning("OnLostFood");
        if (foodsCard.Remove(food))
        {
            if (foodsChara.TryGetValue(food, out Chara chara))
            {
                chara.MoveZone(self.currentZone);
                chara.SetHidden(false);
                CAM.GetAddon(chara).WillConsumeTurn = false;
                chara.MoveImmediate(self.pos);
                foodsChara.Remove(food);

                Msg.SayRaw(self.NameSimple + "丢失了食物: " + chara.NameSimple);
            }
            else
                Msg.SayRaw(self.NameSimple + "丢失了食物: " + food.NameSimple);
        }
    }


    void Eat(Card target)
    {
        if (target.isChara)
        {
            var chara = target.Chara;
            var thing = MakeAsFood(target);

            CAM.GetAddon(chara).WillConsumeTurn = true;
            chara.SetHidden(true);
            chara.SetNoGoal();
            chara.MoveImmediate(new Point(0, 0));

            target = thing;
            foodsChara.Add(target, chara);

        }
        else
        {
            //target.isWeightChanged = true;
            //target.c_weight =(int)((float)target.c_weight / self.c_weight * 1000);
            //target.SetDirtyWeight();
        }

        foodsCard.Add(target);

        self.SetAIImmediate(new ErikaAct_Eat
        {
            target = target
        });
    }


    public Card MakeAsFood(Card c)
    {
        var thing = ThingGen.Create("figure");
        thing.MakeFigureFrom(c.id);
        //thing.MakeRefFrom(c);
        thing.ChangeMaterial(c.material.id);
        SourceRace.Row race = c.Chara.race;

        if (race.IsUndead)
        {
            thing.elements.ModBase(73, -20);
        }

        //thing.isWeightChanged = true;
        //thing.c_weight =(int)((float)c.c_weight / self.c_weight * 1000);
        //thing.SetDirtyWeight();

        thing.blessedState = BlessedState.Normal;
        thing.LV = c.LV;
        return thing;
    }
}




public class ErikaAct_Eat : AI_Eat
{
    public override bool CancelWhenDamaged => false;

    public override bool CancelWhenMoved => false;

    public override Status Cancel()
    {
        Debug.LogWarning("ErikaAct_Eat Cancel " + owner.NameSimple);
        CAM.GetAddon(owner).OnLostFood(target);
        return base.Cancel();
    }

    public override void OnCancel()
    {
        base.OnCancel();

    }

}